package com.rambo.spm.common.aop;

import com.rambo.spm.sys.dao.inter.po.OperationLogPO;
import com.rambo.spm.sys.service.inter.OperationLogService;
import com.xiaoleilu.hutool.crypto.SecureUtil;
import com.xiaoleilu.hutool.http.HttpUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

@Aspect
@Component
public class OperationLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(OperationLogAspect.class);

    @Resource
    private OperationLogService operationLogService;

    @Pointcut("@annotation(com.rambo.spm.common.aop.OperationLog)")
    public void operationLogAspect() {

    }

    @Before("operationLogAspect()")
    public void doBefore(JoinPoint joinPoint) {
        logger.info("before aop：{}", joinPoint);
        gePointMsg(joinPoint);
    }

    @Around("operationLogAspect()")
    public Object doAround(ProceedingJoinPoint point) throws NoSuchMethodException {
        logger.info("Around:{}", point);
        Object proceed = null;
        try {
            proceed = point.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            doAfterThrowing(point, throwable);
        }

        Object pointTarget = point.getTarget();
        Signature pointSignature = point.getSignature();

        String targetName = pointTarget.getClass().getName();
        String methodName = pointSignature.getName();
        Method method = pointTarget.getClass().getMethod(pointSignature.getName(), ((MethodSignature) pointSignature).getParameterTypes());
        OperationLog methodAnnotation = method.getAnnotation(OperationLog.class);
        String operationModular = methodAnnotation.operationModular();
        String operationContent = methodAnnotation.operationContent();

        OperationLogPO log = new OperationLogPO();
        log.setOperUserid(SecureUtil.simpleUUID());
        log.setOperUserip(HttpUtil.getClientIP(getHttpReq()));
        log.setOperModular(operationModular);
        log.setOperContent(operationContent);
        log.setOperClass(targetName);
        log.setOperMethod(methodName);
        log.setOperTime(new Date());
        log.setOperResult("Y");
        operationLogService.insert(log);
        return proceed;
    }

    private void doAfterThrowing(JoinPoint pjp, Throwable throwable) throws NoSuchMethodException {
        Object pointTarget = pjp.getTarget();
        Signature pointSignature = pjp.getSignature();

        String targetName = pointTarget.getClass().getName();
        String methodName = pointSignature.getName();
        Method method = pointTarget.getClass().getMethod(pointSignature.getName(), ((MethodSignature) pointSignature).getParameterTypes());
        OperationLog methodAnnotation = method.getAnnotation(OperationLog.class);
        String operationModular = methodAnnotation.operationModular();
        String operationContent = methodAnnotation.operationContent();

        OperationLogPO log = new OperationLogPO();
        log.setOperUserid(SecureUtil.simpleUUID());
        log.setOperUserip(HttpUtil.getClientIP(getHttpReq()));
        log.setOperModular(operationModular);
        log.setOperContent(operationContent);
        log.setOperClass(targetName);
        log.setOperMethod(methodName);
        log.setOperTime(new Date());
        log.setOperResult("N");
        log.setErrorMsg(throwable.toString());
        operationLogService.insert(log);
    }

    @After("operationLogAspect()")
    public void doAfter(JoinPoint pjp) {
        logger.info("@After:{}", pjp);
    }

    @AfterReturning("operationLogAspect()")
    public void doAfterReturning(JoinPoint point) {
        logger.info("@AfterReturning:{}", point);
    }

    private void gePointMsg(JoinPoint joinPoint) {
        logger.info("切点所在位置:{}", joinPoint.toString());
        logger.info("切点所在位置的简短信息:{}", joinPoint.toShortString());
        logger.info("切点所在位置的全部信息:{}", joinPoint.toLongString());
        logger.info("切点AOP代理对象:{}", joinPoint.getThis());
        logger.info("切点目标对象:{}", joinPoint.getTarget());
        logger.info("切点被通知方法参数列表:{}", joinPoint.getArgs());
        logger.info("切点签名:{}", joinPoint.getSignature());
        logger.info("切点方法所在类文件中位置:{}", joinPoint.getSourceLocation());
        logger.info("切点类型:{}", joinPoint.getKind());
        logger.info("切点静态部分:{}", joinPoint.getStaticPart());
    }

    private HttpServletRequest getHttpReq() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        return servletRequestAttributes.getRequest();
    }
}